<?php
/**
 * Defines the cube
 *
 * @package CubeModel
 * @since   Oct 19, 2006
 * @author  Christophe Thiriot
 */


/**
 * The class of the cube
 *
 * @package CubeModel
*/
class cube 
{
    const MOVE_U = 'U';
    const MOVE_D = 'D';
    const MOVE_R = 'R';
    const MOVE_L = 'L';
    const MOVE_F = 'F';
    const MOVE_B = 'B';
    const MOVE_M = 'M';
    const MOVE_E = 'E';
    const MOVE_S = 'S';
    const MOVE_SEPARATOR = ' ';
    
    /**
     * @var array
     */
    private $tabCenters;
    /**
     * @var array
     */
    private $tabEdges;
    /**
     * @var array
     */
    private $tabCorners;
    /**
     * @var array
     *
     * The list of differents simple turns and their result
     * Example of edges for U :
     * 1 goes to 4, 4 goes to 3, 3 goes to 2 and 2 goes to 1
     * - is for ccw o flipped for edges
     * + is for cw
     */
    private $turns = array(
        self::MOVE_U => array(
            'edges'   => array( ' 1', ' 4', ' 3', ' 2'),
            'corners' => array( ' 1', ' 4', ' 3', ' 2')
        ),
        self::MOVE_D => array(
            'edges'   => array( ' 9',' 10',' 11',' 12'),
            'corners' => array( ' 5', ' 6', ' 7', ' 8')
        ),
        self::MOVE_R => array(
            'edges'   => array( '-2', '-7','-10', '-6'),
            'corners' => array( '+2', '-3', '+7', '-6')
        ),
        self::MOVE_L => array(
            'edges'   => array( '-4', '-5','-12', '-8'),
            'corners' => array( '-1', '+5', '-8', '+4')
        ),
        self::MOVE_F => array(
            'edges'   => array( ' 1', ' 6', ' 9', ' 5'),
            'corners' => array( '+1', '-2', '+6', '-5')
        ),
        self::MOVE_B => array(
            'edges'   => array( ' 3', ' 8',' 11', ' 7'),
            'corners' => array( '+3', '-4', '+8', '-7')
        ),
        self::MOVE_M => array(
            'edges'   => array( '-1', '-9','-11', '-3')
        ),
        self::MOVE_E => array(
            'edges'   => array( '-5', '-6','-7', '-8')
        ),
        self::MOVE_S => array(
            'edges'   => array( '-2', '-10','-12', '-4')
        )

    );


    /**
     * Constructor
     */
    public function __construct()
    {
        for ($index=1; $index<=center::MAX_CENTER; $index++) {
            $this->tabCenters[$index] = new center($index);
        }
        for ($index=1; $index<=edge::MAX_EDGE; $index++) {
            $this->tabEdges[$index] = new edge($index);
        }
        for ($index=1; $index<=corner::MAX_CORNER; $index++) {
            $this->tabCorners[$index] = new corner($index);
        }
    }

    /**
     * just doing base turns, no combination
     * handling orientation, too
     * @param string $turn One letter of a turn
     */
    public function turn($mov) 
    {      
        if (array_key_exists($mov, $this->turns)) 
        {
	        // extracting the positions and orientations
	        $tabPositions = $this->_extractPositions($this->turns[$mov]);
	        $tabOrientations = $this->_extractOrientation($this->turns[$mov]);

            // orientation handling
            foreach ($tabOrientations['edges'] as $key=>$value) 
            {
                if ($value === '-') 
                {
					$this->tabEdges[$tabPositions['edges'][$key]]->toggleOrientation();
				}
			}
            if (1 < count($tabOrientations)) 
            {
                foreach ($tabOrientations['corners'] as $key=>$value) {
    				if ($value === '-') {
    					$this->tabCorners[$tabPositions['corners'][$key]]->turnCCW();
    				} else if ($value === '+') {
    					$this->tabCorners[$tabPositions['corners'][$key]]->turnCW();
    				}
    			}
            }

			// positions handling
	        $temp = $this->tabEdges[$tabPositions['edges'][3]];
	        for ($i = 3; $i > 0; $i--) {
			    $this->tabEdges[$tabPositions['edges'][$i]] = $this->tabEdges[$tabPositions['edges'][$i - 1]];
			}
            $this->tabEdges[$tabPositions['edges'][0]] = $temp;
            if (1 < count($tabPositions)) {
    	        $temp = $this->tabCorners[$tabPositions['corners'][3]];
    	        for ($i = 3; $i > 0; $i--) {
    			    $this->tabCorners[$tabPositions['corners'][$i]] = $this->tabCorners[$tabPositions['corners'][$i - 1]];
    			}
                $this->tabCorners[$tabPositions['corners'][0]] = $temp;
            }
        } else {
            throw new Exception('cube::turn : unknown move : ' . $mov);
        }
    }

	/**
	 * @return array positions of a permutation
	 */
    private function _extractPositions($tabturn) {
    	$tabresult = array();
    	foreach ($tabturn as $cubietype=>$tab) {
			foreach ($tab as $value) {
				$tabresult[$cubietype][] = (int) substr($value, 1);
			}
    	}
    	return $tabresult;
    }

	/**
	 * @return array orientations of a permutation
	 */
    private function _extractOrientation($tabturn) {
    	$tabresult = array();
    	foreach ($tabturn as $cubietype=>$tab) {
			foreach ($tab as $value) {
				$tabresult[$cubietype][] = $value[0];
			}
    	}
    	return $tabresult;
    }

    /**
     * Serialization of the cube
     * @return string
     */
    public function __toString() {
        /*$mystring = '( ';
        foreach ($this->tabCenters as $cubie) {
            $mystring.= $cubie->getOriginPosition() . ' ';
        }
        $mystring.= ') ';*/

        $mystring = '(';
        foreach ($this->tabEdges as $cubie) {
            $mystring.= $cubie->getOriginPosition() . $cubie->orientToString() . ' ';
        }
        $mystring = chop($mystring);
        $mystring.= ') (';
        foreach ($this->tabCorners as $cubie) {
            $mystring.= $cubie->getOriginPosition() . $cubie->orientToString() . ' ';
        }
        $mystring = chop($mystring);
        $mystring.= ")";
        return $mystring;
    }

    /**
     * execute an algo 
     * @param string $strturn
     */
    public function alg($strturn)
    {
        $tturn = explode(' ', $strturn);
        foreach ($tturn as $key => $value)
        {
            $this->turn($value);
        }
    }

}
?>
